﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;

namespace FCNS.Media
{
    /// <summary>
    /// 图像类
    /// </summary>
    public class ImageCompare
    {
        /// <summary>
        /// 比较图片是否相同
        /// </summary>
        /// <param name="firstImage">第一张图</param>
        /// <param name="secondImage">第二张图</param>
        /// <returns></returns>
        public static bool ImageCompareBool(Bitmap firstImage, Bitmap secondImage)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                String firstBitmap = Convert.ToBase64String(ms.ToArray());
                ms.Position = 0;

                secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                String secondBitmap = Convert.ToBase64String(ms.ToArray());

                if (firstBitmap.Equals(secondBitmap))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 图像颜色
        /// </summary>
        [StructLayout(LayoutKind.Explicit)]
        private struct ICColor
        {
            [FieldOffset(0)]
            public byte B;
            [FieldOffset(1)]
            public byte G;
            [FieldOffset(2)]
            public byte R;
        }

        /// <summary>
        /// 比较两个图像不同的区域
        /// </summary>
        /// <param name="bmp1">第一张图</param>
        /// <param name="bmp2">第二张图</param>
        /// <param name="block">图像比较用的块大小</param>
        /// <returns>图像不同的区域</returns>
        public static List<Rectangle> ImageCompareRect(Bitmap bmp1, Bitmap bmp2, Size block)
        {
                List<Rectangle> rects = new List<Rectangle>();
            //默认 block=20*20
                PixelFormat pf = PixelFormat.Format24bppRgb;

                BitmapData bd1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height), ImageLockMode.ReadOnly, pf);
                BitmapData bd2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), ImageLockMode.ReadOnly, pf);
                
                try
                {
                    unsafe
                    {
                        int w = 0, h = 0;

                        while (h < bd1.Height && h < bd2.Height)
                        {
                            byte* p1 = (byte*)bd1.Scan0 + h * bd1.Stride;
                            byte* p2 = (byte*)bd2.Scan0 + h * bd2.Stride;

                            w = 0;
                            while (w < bd1.Width && w < bd2.Width)
                            {
                                //按块大小进行扫描
                                for (int i = 0; i < block.Width; i++)
                                {
                                    int wi = w + i;
                                    if (wi >= bd1.Width || wi >= bd2.Width) break;

                                    for (int j = 0; j < block.Height; j++)
                                    {
                                        int hj = h + j;
                                        if (hj >= bd1.Height || hj >= bd2.Height) break;

                                        ICColor* pc1 = (ICColor*)(p1 + wi * 3 + bd1.Stride * j);
                                        ICColor* pc2 = (ICColor*)(p2 + wi * 3 + bd2.Stride * j);

                                        if (pc1->R != pc2->R || pc1->G != pc2->G || pc1->B != pc2->B)
                                        {
                                            //当前块有某个象素点颜色值不相同.也就是有差异.

                                            int bw = Math.Min(block.Width, bd1.Width - w);
                                            int bh = Math.Min(block.Height, bd1.Height - h);
                                            rects.Add(new Rectangle(w, h, bw, bh));

                                            goto E;
                                        }
                                    }
                                }
                            E:
                                w += block.Width;
                            }

                            h += block.Height;
                        }
                    }
                }
                finally
                {
                    bmp1.UnlockBits(bd1);
                    bmp2.UnlockBits(bd2);
                }
                return rects;
        }

        /// <summary>   
        /// 判断图形里是否存在另外一个图形 并返回所在位置,背景改变会失效  
        /// </summary>   
        /// <param name="p_SourceBitmap">原始图形</param>   
        /// <param name="p_PartBitmap">小图形</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns>坐标</returns>   
        public static  Point GetImageContains(Bitmap sourceBitmap, Bitmap partBitmap, int pFloat)
        {
            int sourceWidth = sourceBitmap.Width;
                int sourceHeight = sourceBitmap.Height;

                int partWidth = partBitmap.Width;
                int partHeight = partBitmap.Height;

                Bitmap _SourceBitmap = new Bitmap(sourceWidth, sourceHeight);
                Graphics _Graphics = Graphics.FromImage(_SourceBitmap);
                _Graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, sourceWidth, sourceHeight));
                _Graphics.Dispose();
                BitmapData _SourceData = _SourceBitmap.LockBits(new Rectangle(0, 0, sourceWidth, sourceHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                byte[] _SourceByte = new byte[_SourceData.Stride * sourceHeight];
                Marshal.Copy(_SourceData.Scan0, _SourceByte, 0, _SourceByte.Length);  //复制出p_SourceBitmap的相素信息    

                Bitmap _PartBitmap = new Bitmap(partWidth, partHeight);
                _Graphics = Graphics.FromImage(_PartBitmap);
                _Graphics.DrawImage(partBitmap, new Rectangle(0, 0, partWidth, partHeight));
                _Graphics.Dispose();
                BitmapData _PartData = _PartBitmap.LockBits(new Rectangle(0, 0, partWidth, partHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                byte[] _PartByte = new byte[_PartData.Stride * partHeight];
                Marshal.Copy(_PartData.Scan0, _PartByte, 0, _PartByte.Length);   //复制出p_PartBitmap的相素信息    

                for (int i = 0; i != sourceHeight; i++)
                {
                    if (sourceHeight - i < partHeight) return new Point(-1, -1);  //如果 剩余的高 比需要比较的高 还要小 就直接返回                
                    int _PointX = -1;    //临时存放坐标 需要包正找到的是在一个X点上   
                    bool _SacnOver = true;   //是否都比配的上   
                    for (int z = 0; z != partHeight - 1; z++)       //循环目标进行比较   
                    {
                        int _TrueX = GetImageContains(_SourceByte, _PartByte, i * _SourceData.Stride, sourceWidth, partWidth, pFloat);
                        if (_TrueX == -1)   //如果没找到    
                        {
                            _PointX = -1;    //设置坐标为没找到   
                            _SacnOver = false;   //设置不进行返回   
                            break;
                        }
                        else
                        {
                            if (z == 0) _PointX = _TrueX;
                            if (_PointX != _TrueX)   //如果找到了 也的保证坐标和上一行的坐标一样 否则也返回   
                            {
                                _PointX = -1;//设置坐标为没找到   
                                _SacnOver = false;  //设置不进行返回   
                                break;
                            }
                        }
                    }
                    if (_SacnOver) return new Point(_PointX, i);
                }
                return new Point(-1, -1);
                  }
        /// <summary>   
        /// 判断图形里是否存在另外一个图形 所在行的索引   
        /// </summary>   
        /// <param name="p_Source">原始图形数据</param>   
        /// <param name="p_Part">小图形数据</param>   
        /// <param name="p_SourceIndex">开始位置</param>   
        /// <param name="p_SourceWidth">原始图形宽</param>   
        /// <param name="p_PartWidth">小图宽</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns>所在行的索引 如果找不到返回-1</returns>   
        private static int GetImageContains(byte[] p_Source, byte[] p_Part, int p_SourceIndex, int p_SourceWidth, int p_PartWidth, int p_Float)
        {
            int _PartIndex = 0;
            int _SourceIndex = p_SourceIndex;
            for (int i = 0; i < p_SourceWidth; i++)
            {
                if (p_SourceWidth - i < p_PartWidth) return -1;
                Color _CurrentlyColor = Color.FromArgb((int)p_Source[_SourceIndex + 3], (int)p_Source[_SourceIndex + 2], (int)p_Source[_SourceIndex + 1], (int)p_Source[_SourceIndex]);
                Color _CompareColoe = Color.FromArgb((int)p_Part[3], (int)p_Part[2], (int)p_Part[1], (int)p_Part[0]);
                _SourceIndex += 4;

                bool _ScanColor = ScanColor(_CurrentlyColor, _CompareColoe, p_Float);

                if (_ScanColor)
                {
                    _PartIndex += 4;
                    int _SourceRVA = _SourceIndex;
                    bool _Equals = true;
                    for (int z = 0; z != p_PartWidth - 1; z++)
                    {
                        _CurrentlyColor = Color.FromArgb((int)p_Source[_SourceRVA + 3], (int)p_Source[_SourceRVA + 2], (int)p_Source[_SourceRVA + 1], (int)p_Source[_SourceRVA]);
                        _CompareColoe = Color.FromArgb((int)p_Part[_PartIndex + 3], (int)p_Part[_PartIndex + 2], (int)p_Part[_PartIndex + 1], (int)p_Part[_PartIndex]);

                        if (!ScanColor(_CurrentlyColor, _CompareColoe, p_Float))
                        {
                            _PartIndex = 0;
                            _Equals = false;
                            break;
                        }
                        _PartIndex += 4;
                        _SourceRVA += 4;
                    }
                    if (_Equals) return i;
                }
                else
                {
                    _PartIndex = 0;
                }
            }
            return -1;
        }

        /// <summary>   
        /// 检查色彩(可以根据这个更改比较方式   
        /// </summary>   
        /// <param name="p_CurrentlyColor">当前色彩</param>   
        /// <param name="p_CompareColor">比较色彩</param>   
        /// <param name="p_Float">溶差</param>   
        /// <returns></returns>   
        private static bool ScanColor(Color p_CurrentlyColor, Color p_CompareColor, int p_Float)
        {
            int _R = p_CurrentlyColor.R;
            int _G = p_CurrentlyColor.G;
            int _B = p_CurrentlyColor.B;

            return (_R <= p_CompareColor.R + p_Float && _R >= p_CompareColor.R - p_Float) && (_G <= p_CompareColor.G + p_Float && _G >= p_CompareColor.G - p_Float) && (_B <= p_CompareColor.B + p_Float && _B >= p_CompareColor.B - p_Float);

        }
    }
}